
Alkaline.Processing.FunctionInstance = function(ast, parentContext) {
	this.value = ast;
	this.argumentsTree = ast.children[ast.children.length-2];
	this.bodyTree = ast.children[ast.children.length-1];
	this.innerLexicalContext = new Alkaline.Processing.LexicalContext(parentContext, this.bodyTree);
	this.innerLexicalContext.functionReference = this;
	this.isGenerator = false;
	this.yields = [];
	this.builder = parentContext.builder;
	// arguments object
	var argsVar = new Alkaline.Processing.VariableDefinition("arguments", Alkaline.Processing.VariableDefinition.SOURCE_TYPE.NATIVE);
	this.innerLexicalContext.addVariable(argsVar);
	argsVar.setValue(Alkaline.Processing.Condition.ALWAYS, new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.ARGUMENTS_ARRAY, null), -1, -1);
	this.argumentsArray = argsVar;
}
Alkaline.Processing.FunctionInstance.prototype = new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.FUNCTION, null);
Alkaline.Processing.FunctionInstance.prototype.addNewArgument = function() {
	var varRef = this.innerLexicalContext.getNewVariableAvailableInChildren(true);
	var declToken = this.builder.Id(varRef.name);
	this.builder.addChild(this.argumentsTree, declToken);
	varRef.declarationToken = declToken;
	return varRef;	
}
Alkaline.Processing.FunctionInstance.prototype.captureUndeclaredGlobals = function() {
	if (!this.bodyTree.children || !this.bodyTree.children.length)
		return;	// nothing to do
	var globalContext = this.innerLexicalContext;
	while (globalContext.inheritedContext)
		globalContext = globalContext.inheritedContext;
	for (var i = 0; i < globalContext.context.length; i++)
		if (globalContext.context[i].sourceType == Alkaline.Processing.VariableDefinition.SOURCE_TYPE.DETECTED) {
			var varRef = globalContext.context[i];
			var bucket = [];
			for (var j = 0; j < varRef.referencedFrom.length; j++) {
				var node = varRef.referencedFrom[j];
				while (node != this.bodyTree && node.parent)
					node = node.parent;
				if (node == this.bodyTree)
					bucket.push(varRef.referencedFrom[j]);
			}
			if (bucket.length) {
				// declare the variable in the scope of the function
				this.builder.addChild(this.bodyTree, this.builder.Var([varRef.name]), 0);

				var newVar = this.innerLexicalContext.getVariable(varRef.name);
				// update all the references
				for (var j = 0; j < bucket.length; j++) {
					newVar.addReference(bucket[j]);
					bucket[j].objectValue = newVar;
				}
				// remove the renamed references
				if (varRef.referencedFrom.length == bucket.length) {
					// remove the phantom variable completely
					globalContext.removeVariable(varRef);
				} else {
					// remove only the references
					for (var j = 0; j < bucket.length; j++)
						varRef.removeReference(bucket[j]);
				}
			}
		}
}
